﻿using System;
using System.Text;

namespace FlyRemote.Base
{
    /// <summary>
    ///  用来 定义搜索 或者 显示的类型, 同时对应 c# 的内置类型
    ///  结构体大小 是8
    /// </summary>
    public class DataType
    {

        #region 数据 
        // 将 枚举类型序列化为文字 , 而不是数字 
        public TypeCode Code;

        private short _size;

        public bool IsRef { get; set; }

        #endregion



        public enum TypeCode : sbyte
        {

            Empty = -1,

            u8,

            i16,

            i32,

            i64,

            f32,

            f64,

            ByteArray,

            Ansi,

            Utf8,

            Utf16,


            // 无符号 暂时不考虑
            i8,
            u16,
            u32,
            u64,

        }



        //public static void BindToComboBox(System.Windows.Forms.ComboBox comboBox)
        //{
        //    comboBox.DataSource = new System.Windows.Forms.BindingSource { DataSource = DataType.GetTypeCodes() };
        //    comboBox.DisplayMember = "Value";
        //    comboBox.ValueMember = "Key";

        //}




        /// 数据大小 必须在创建时确定 

        public DataType(TypeCode code, short size = 0)
        {
            this.Code = code;
            this._size = size;
            this.IsRef = false;
        }

        public DataType(bool is_pointer, TypeCode code, short size = 0)
        {
            this.Code = code;
            this._size = size;
            this.IsRef = is_pointer;
        }




        /// <summary>
        /// 对于 文本类型，指的是字符个数 
        /// </summary>
        public short Size
        {

            get
            {
                switch (this.Code)
                {
                    case TypeCode.u8:
                    case TypeCode.i8:
                        return 1;
                    case TypeCode.i16:
                    case TypeCode.u16:
                        return 2;
                    case TypeCode.i32:
                    case TypeCode.u32:
                    case TypeCode.f32:
                        return 4;

                    case TypeCode.i64:
                    case TypeCode.u64:
                    case TypeCode.f64:
                        return 8;


                    case TypeCode.ByteArray:
                    case TypeCode.Ansi:
                    case TypeCode.Utf8:
                    case TypeCode.Utf16:
                        return _size;
                }

                return 0;
            }
        }



        /// <summary>
        ///  仅可比较 是否相等
        /// </summary>
        public bool OnlyEqualsable()
        {
            // 除了相等之外, 是否允许其它比较  

            switch (this.Code)
            {
                case TypeCode.ByteArray:
                case TypeCode.Ansi:
                case TypeCode.Utf16:
                case TypeCode.Utf8:
                    return true;

                default:
                    return false;
            }

        }



        /// --------------
        /// 要想做 case 比较  , 必须使用这种写法 
        ///  case DataType type when type == DataType.Byte:
        /// --------------

        public static readonly DataType Empty = new DataType(TypeCode.Empty);
        public static readonly DataType u8 = new DataType(TypeCode.u8);
        public static readonly DataType i8 = new DataType(TypeCode.i8);

        public static readonly DataType i16 = new DataType(TypeCode.i16);
        public static readonly DataType u16 = new DataType(TypeCode.u16);

        public static readonly DataType u32 = new DataType(TypeCode.u32);
        public static readonly DataType i32 = new DataType(TypeCode.i32);

        public static readonly DataType u64 = new DataType(TypeCode.u64);
        public static readonly DataType i64 = new DataType(TypeCode.i64);

        public static readonly DataType f32 = new DataType(TypeCode.f32);
        public static readonly DataType f64 = new DataType(TypeCode.f64);


        #region 扩展方法 

        public override string ToString()
        {
            return $"{Code},{Size}";
        }


        // 从 c# 基础类型 转换为 我们的类型  
        public static implicit operator DataType(Type t)
        {
            if (t == typeof(Byte))
                return DataType.u8;

            if (t == typeof(SByte))
                return DataType.i8;

            if (t == typeof(Int16))
                return DataType.i16;

            if (t == typeof(UInt16))
                return DataType.u16;

            if (t == typeof(Int32))
                return DataType.i32;

            if (t == typeof(UInt32))
                return DataType.u32;

            if (t == typeof(Int64))
                return DataType.i64;

            if (t == typeof(UInt64))
                return DataType.u64;

            if (t == typeof(Single))
                return DataType.f32;

            if (t == typeof(Double))
                return DataType.f64;

            if (t == typeof(string))
                return new DataType(TypeCode.Utf8);

            if (t == typeof(byte[]))
                return new DataType(TypeCode.ByteArray);


            return DataType.Empty;
        }

        /// <summary>
        ///   Type 类型
        /// </summary>
        public Type ToType()
        {
            switch (this.Code)
            {
                case TypeCode.u8:
                    return typeof(Byte);
                case TypeCode.i8:
                    return typeof(SByte);
                case TypeCode.i16:
                    return typeof(Int16);
                case TypeCode.u16:
                    return typeof(UInt16);
                case TypeCode.i32:
                    return typeof(Int32);
                case TypeCode.u32:
                    return typeof(UInt32);
                case TypeCode.i64:
                    return typeof(Int64);
                case TypeCode.u64:
                    return typeof(UInt64);
                case TypeCode.f32:
                    return typeof(Single);
                case TypeCode.f64:
                    return typeof(Double);

                case TypeCode.Ansi:
                case TypeCode.Utf8:
                case TypeCode.Utf16:
                    return typeof(string);

                case TypeCode.ByteArray:
                    return typeof(byte[]);
            }


            return null;

        }




        public static Boolean operator ==(DataType self, DataType other)
        {
            if (Object.ReferenceEquals(self, other))
                return true;

            if (other is DataType)
                return self.Code == other.Code;

            return false;
        }

        public static Boolean operator !=(DataType self, DataType other)
        {
            return !(self == other);
        }

        public override int GetHashCode()
        {
            return this.Code.GetHashCode();
        }

        public override Boolean Equals(Object dataType)
        {
            return (dataType is DataType) && this.Code == ((DataType)dataType).Code;
        }
        public Boolean Equals(DataType dataType)
        {
            return this.Code == ((DataType)dataType).Code;
        }


        #endregion


        public object AddValue(object value, int val)
        {

            try
            {

                switch (this.Code)
                {

                    case TypeCode.u8:
                        return (byte)((byte)value + val);
                    case TypeCode.i8:
                        return (sbyte)((sbyte)value + val);
                    case TypeCode.i16:
                        return (short)((short)value + val);
                    case TypeCode.u16:
                        return (ushort)((ushort)value + val);
                    case TypeCode.i32:
                        return (int)((int)value + val);
                    case TypeCode.u32:
                        return (uint)((uint)value + val);
                    case TypeCode.i64:
                        return (long)((long)value + val);
                    case TypeCode.u64:
                        return (ulong)((ulong)value + (ulong)val);
                    case TypeCode.f32:
                        return (float)((float)value + val);
                    case TypeCode.f64:
                        return (double)((double)value + val);

                }
            }
            catch
            {
            }
            return null;

        }




        /// <summary>
        /// 解析数据到指定类型 , 同时处理数据长度 
        /// </summary>
        public object ParseValue(string str, bool isHex)
        {

            int fb = isHex ? 16 : 10;

            try
            {

                switch (this.Code)
                {

                    case TypeCode.u8:
                        return Convert.ToByte(str.Trim(), fb);
                    case TypeCode.i8:
                        return Convert.ToSByte(str.Trim(), fb);
                    case TypeCode.i16:
                        return Convert.ToInt16(str.Trim(), fb);
                    case TypeCode.u16:
                        return Convert.ToUInt16(str.Trim(), fb);
                    case TypeCode.i32:
                        return Convert.ToInt32(str.Trim(), fb);
                    case TypeCode.u32:
                        return Convert.ToUInt32(str.Trim(), fb);
                    case TypeCode.i64:
                        return Convert.ToInt64(str.Trim(), fb);
                    case TypeCode.u64:
                        return Convert.ToUInt64(str.Trim(), fb);
                    case TypeCode.f32:
                        return Convert.ToSingle(str.Trim());
                    case TypeCode.f64:
                        return Convert.ToDouble(str.Trim());
                    //case TypeCode.ByteArray:
                    //    var arr = str.Trim().ToByteArr();
                    //    this._size = (byte)arr.Length;
                    //    return arr;

                    case TypeCode.Ansi:
                    case TypeCode.Utf8:
                        this._size = (byte)str.Length;
                        return Encoding.UTF8.GetBytes(str);
                    case TypeCode.Utf16:
                        this._size = (byte)str.Length;
                        return Encoding.Unicode.GetBytes(str);

                }
            }
            catch
            {
                return null;
            }
            return null;
        }


        /// <summary>
        /// 以指定类型 格式化 
        /// </summary>
        public string FmtValue(object value, bool isHex, bool align = false)
        {
            if (value == null)
                return string.Empty;

            if (value as string == "?")
                return "?";

            if (isHex)
            {

                switch (this.Code)
                {

                    case TypeCode.i8:
                        return ((sbyte)value).ToString(align ? "X2" : "X");
                    case TypeCode.u8:
                        return ((byte)value).ToString(align ? "X2" : "X");

                    case TypeCode.i16:
                        return ((short)value).ToString(align ? "X4" : "X");
                    case TypeCode.u16:
                        return ((ushort)value).ToString(align ? "X4" : "X");

                    case TypeCode.i32:
                        return ((int)value).ToString(align ? "X8" : "X");
                    case TypeCode.u32:
                        return ((uint)value).ToString(align ? "X8" : "X");

                    case TypeCode.i64:
                        return ((long)value).ToString(align ? "X16" : "X");
                    case TypeCode.u64:
                        return ((ulong)value).ToString(align ? "X16" : "X");

                    case TypeCode.f32:
                        return ((float)value).ToString(align ? "X8" : "X");

                    case TypeCode.f64:
                        return ((double)value).ToString(align ? "X16" : "X");
                }
            }

            switch (this.Code)
            {
                case TypeCode.i8:
                    return ((SByte)value).ToString();
                case TypeCode.u8:
                    return ((byte)value).ToString();

                case TypeCode.i16:
                    return ((Int16)value).ToString();
                case TypeCode.u16:
                    return ((UInt16)value).ToString();

                case TypeCode.i32:
                    return ((Int32)value).ToString();
                case TypeCode.u32:
                    return ((UInt32)value).ToString();

                case TypeCode.i64:
                    return ((Int64)value).ToString();
                case TypeCode.u64:
                    return ((UInt64)value).ToString();

                case TypeCode.f32:
                    return ((float)value).ToString();

                case TypeCode.f64:
                    return ((double)value).ToString();

                //case TypeCode.ByteArray:
                //    return ((byte[])value).ToHexString();

                case TypeCode.Ansi:
                case TypeCode.Utf8:
                case TypeCode.Utf16:
                    return value.ToString();
            }


            return string.Empty;
        }



        /// <summary>
        /// 转换字节数组 到其它类型 
        /// </summary>
        /// <typeparam name="T">The data type of the object.</typeparam>
        /// <param name="byteArray">The array of bytes.</param>
        public static T FromBytes<T>(Byte[] byteArray)
        {
            Type type = typeof(T);

            if (type == typeof(Boolean))
                return (T)(Object)BitConverter.ToBoolean(byteArray, 0);
            else if (type == typeof(Byte))
                return (T)(Object)byteArray[0];
            else if (type == typeof(Char))
                return (T)(Object)BitConverter.ToChar(byteArray, 0);
            else if (type == typeof(Int16))
                return (T)(Object)BitConverter.ToInt16(byteArray, 0);
            else if (type == typeof(Int32))
                return (T)(Object)BitConverter.ToInt32(byteArray, 0);
            else if (type == typeof(Int64))
                return (T)(Object)BitConverter.ToInt64(byteArray, 0);
            else if (type == typeof(SByte))
                return (T)(Object)unchecked((SByte)byteArray[0]);
            else if (type == typeof(UInt16))
                return (T)(Object)BitConverter.ToUInt16(byteArray, 0);
            else if (type == typeof(UInt32))
                return (T)(Object)BitConverter.ToUInt32(byteArray, 0);
            else if (type == typeof(UInt64))
                return (T)(Object)BitConverter.ToUInt64(byteArray, 0);
            else if (type == typeof(Single))
                return (T)(Object)BitConverter.ToSingle(byteArray, 0);
            else if (type == typeof(Double))
                return (T)(Object)BitConverter.ToDouble(byteArray, 0);
            else
                throw new ArgumentException("Invalid type provided");
        }


    }

}
